#include "GrammarSymbol.h"
#include "Production.h"
#include <iostream>

#define INFO_FLAG

TerminalSymbol epsilo; //全局变量，epsilo
TerminalSymbol dollar("$");

void NonTerminalSymbol::add_production(int pro) {
    NonTerminalSymbol* non_symbol=Production::get_left_most_nonterminal(pro);
    production_table.push_back(pro);
    return;
    //不需要修改左递归了，LR可以解决
    if(this!=non_symbol){
        production_table.push_back(pro);
    }else{
        //要修改左递归
        //S->Sab
        this->linked_non_terminal=make_shared<NonTerminalSymbol>(this->name+"_PLUS");
        //S->X
        PRO_ID new_production_id=Production::insert_production(this->linked_non_terminal.get());
        this->add_production(new_production_id);

        //X->abX | epsilo
        list<GrammarSymbol *> list_grammar=Production::get_production_body_symbol_table(pro);
        assert(list_grammar.size()>=2);
        //X->abX
        list_grammar.erase(list_grammar.begin());
        list_grammar.push_back(this->linked_non_terminal.get());
        PRO_ID add_pro_one= Production::insert_production(list_grammar);
        this->linked_non_terminal.get()->add_production(add_pro_one);
        //X->epsilo
        this->linked_non_terminal.get()->add_production(Production::get_epsilo_production_id());
    }
    
}

void NonTerminalSymbol::calcu_first() {
    // 实现计算FIRST集合的逻辑
    for(auto &elem:this->production_table){
        //对每个产生式求first集合，然后合并他的first_set
        NonTerminalSymbol *non_terminal_symbol= Production::get_left_most_nonterminal(elem);
        if(non_terminal_symbol==this){
            continue; //自己到自己，不求first
        }
        Production::calcu_first(elem);
        auto first_set=Production::get_production_first_set(elem);
        this->first_set.insert(first_set.begin(),first_set.end());
    }
#ifdef INFO_FLAG 
    std::cout<<this->name<<" -- first set : {";
    for(auto elem:this->first_set){
        std::cout<<elem->name<<" , ";
    }
    cout<<"}"<<endl;
#endif
}

void NonTerminalSymbol::calcu_follow(){

    for(auto symbol_ptr:dependent_set_in_follow){
        if(symbol_ptr->dependent_set_in_follow.count(this)){
            //两个互相依赖
            if(symbol_ptr->follow_set==this->follow_set){
                continue;//已经计算过了
            }else{
                this->follow_set.insert(symbol_ptr->follow_set.begin(),symbol_ptr->follow_set.end());
                symbol_ptr->follow_set=this->follow_set;
            }
        }else{
            this->follow_set.insert(symbol_ptr->follow_set.begin(),symbol_ptr->follow_set.end());
        }
    }
#ifdef INFO_FLAG 
    std::cout<<this->name<<" -- follow set : {";
    for(auto elem:this->follow_set){
        std::cout<<elem->name<<" , ";
    }
    cout<<"}"<<endl;
#endif
};

void NonTerminalSymbol::calcu_follow_pre(){
    //计算follow的准备工作，计算出所有的dependent关系,必须当所有的nonterminal调用了calcu_follow_pre之后，才能调用calcu_follow
    if(!Production::follow_flag){
        //没求，先让他们求了
        Production::calcu_follow();
    }
    // 实现计算FOLLOW集合的逻辑
    // 现对每个解析式求follow
    for(PRO_ID id:production_table){;
        list<GrammarSymbol*> lt= Production::get_production_body_symbol_table(id);
        auto it=lt.rbegin();
        //TODO 这里可能会出现 X--> aX  这样相互依赖的情况，没解决，只是报错了
        while(it !=lt.rend() && (*it)->type==NON_TERMINAL){
            NonTerminalSymbol *non_terminal=static_cast<NonTerminalSymbol*>(*it);
            // non_terminal->follow_set.insert(this->follow_set.begin(),this->follow_set.end());
            //先求依赖关系，然后在合并set
            non_terminal->dependent_set_in_follow.insert(this);
            if(non_terminal->first_set.count(&epsilo) ){
                it++;
            }else{
                break;
            }
        }
    }
};

void NonTerminalSymbol::get_info() {
    std::cout << name << " -> ";
    auto it = production_table.begin();
    std::cout << Production::get_info(*it);
    ++it;
    for (; it != production_table.end(); ++it) {
        std::cout << " | " << Production::get_info(*it);
    }
    std::cout << std::endl;
    if(linked_non_terminal.use_count()>0){
        this->linked_non_terminal.get()->get_info();
    }
}
